#include <stdio.h>
#include <sys/io.h>

/*
  connections:
  STROBE   --> CLK
  AUTOFEED --> CLR
  ERR <------- DATA
 */


/* #define REALLY_SLOW_IO */

/* parallel port address */
#define port_base       0x378
#define port_status     port_base + 1
#define port_control    port_base + 2
/* data byte out */
#define port_out(byte)  outb_p(byte,port_base)
/* control register bits */
#define update_ctl      outb_p(ctrport,port_control)
#define update_dat      port_out(datport)
#define STROBE_HIGH	ctrport &= ~0x01; update_ctl  /* inverted */
#define STROBE_LOW	ctrport |= 0x01; update_ctl   /* inverted */
#define AUTOFEED_HIGH	ctrport &= ~0x02; update_ctl /* inverted */
#define AUTOFEED_LOW	ctrport |= 0x02; update_ctl  /* inverted */
#define nINIT_HIGH	ctrport |= 0x04; update_ctl  /* non-inverted */
#define nINIT_LOW	ctrport &= ~0x04; update_ctl /* non-inverted */
#define SELECT_HIGH	ctrport &= ~0x08; update_ctl /* inverted */
#define SELECT_LOW	ctrport |= 0x08; update_ctl  /* inverted */

/* needed status register bits */
#define ERR             0x08

#define ACK		0x40

#define bit_in          (inb_p(port_status) & ACK)

/* functions */
#define CLK_HIGH        SELECT_LOW
#define CLK_LOW         SELECT_HIGH
#define CHIP_CLR        nINIT_LOW /* ? */
#define CHIP_ENABLE     nINIT_HIGH

#define TOGGLE_CLOCK    CLK_HIGH; CLK_LOW
#define READ_BIT        bit_in
#define CLEAR_ALL       CHIP_CLR; CHIP_ENABLE


/* parallel port direction control */
#define PCD	        0x20
#define port_write      ctrport &= ~PCD; update_ctl
#define port_read       ctrport |= PCD; update_ctl

static unsigned char ctrport, datport=0;

static void init_port() /* Initialize parallel port */
{
  if (ioperm(port_base,3,1))
  {
    fprintf(stderr, "Error: Couldn't get the port at %x\n", port_base);
    fprintf(stderr, "Note: you MUST run this program as root.\n");
    exit(1);
  }
  ioperm(0x80,3,1);
  ctrport = inb(port_control);
  port_write;
  update_dat;
}


int main()
{
  unsigned int i, j;
  volatile char b;
  init_port();	
  while (1)
  {
    printf("select:\n");    
    printf("1) set CLR HIGH\n");
    printf("2) set CLR LOW\n");
    printf("3) set CLK HIGH\n");
    printf("4) set CLK LOW\n");
    printf("5) set value\n");
    printf("6) get OUTPUT\n");
    printf("7) toggle clock \n");
    printf("8) Read Scan \n");
    printf("9) Print Raw Input Register \n");    
    printf("10) quit\n");
    printf("11) continuous scan\n");
    
    scanf("%d",&i);
    /* i = getchar(); */
    
    switch(i)
      {
      case 1:
	CHIP_ENABLE;
	break;
      case 2:
	CHIP_CLR;
	break;
      case 3:
	CLK_HIGH;
	break;
      case 4:
	CLK_LOW;
	break;
      case 5:
	scanf("%d",&b);
        CLEAR_ALL;
	while(b--) {
          /* TOGGLE_CLOCK; */
	  CLK_HIGH;
	  usleep(100);
	  CLK_LOW;
	}
	break;
      case 6:
	b = bit_in;
	printf("bit = %d\n", b);
	break;
      case 7:
        TOGGLE_CLOCK;
        break;
      case 8:
        printf("Scan:\n");
        for(j=0; j<128; j++) {
          if (j%16==0) printf("\n");
          b = bit_in;
	  
          if (b) {
            printf("X");
          } else {
            printf(".");
          }
	  
	  /* printf("%d",b); */
	  
          TOGGLE_CLOCK;
        }
	printf("\n");        
	break;
      case 9:
        b = inb_p(port_status);
	printf("register = %d\n", b);
        break;
      case 10:
	exit(0);
	break;
      case 11:
	printf("Continuous Scan:\n");
	while (1) {
	    
    	    for(j=0; j<128; j++) {
        	b = bit_in;
                if (b) printf("%d\n",j);
        	TOGGLE_CLOCK;
    	    }
        }
        break;
      }
  }
  return 0;
}
